/**
 * Solution to Mars Rovers Problem
 * 
 * Author:
 *  Wang Sen 	<kelvinxupt@gmail.com> 	2012
 * 
 * This work is licensed under the terms of the GNU GPL v2.
 * 
 * Problem Description:
 *
 * A squad of robotic rovers is to be landed by NASA on a plateau on Mars. This
 * plateau, which is curiously rectangular, must be navigated by the rovers so 
 * that their on-board cameras can get a complete view of the surrounding terra-
 * in to send back to Earth.
 *
 * A rover's positon and location is represented by a combination of x and y co-
 * -ordinates and a letter representing one of the four cardinal compass points.
 * The plateau is divided up into a grid to simplify navigation. An example pos-
 * ition might be 0,0,N, which means the rover is in the bottom left corner and
 * facing North. 
 *
 * Inorder to control a rover, NASA sends a simple string of letters. The possi-
 * ble letters are 'L','R' and 'M'. 'L' and 'R' makes the rover spin 90 degrees
 * left or right respectively, without moving from its current spot. 'M' means
 * move forward one grid point, and maintains the same heading.
 * 
 * Assume that the square directly North from (x,y) is (x,y+1).
 * 
 * INPUT:
 * 
 * The first line of input is the upper-right coordinates of the plateau, the l-
 * ower-left coordinates are assumed to be 0,0.
 * 
 * The rest of the input is information pertaining to the rovers that have been 
 * deployed. Each rover has two lines of input. The first line gives the rover's 
 * positon and the second line is a series of instructions telling the rover how
 * to explore the plateau.
 * 
 * The position is made up of two integers and a letter separated by spaces, co-
 * rresponding to the x and y co-ordinates and the rover's orientation.
 * 
 * Each rover will be finished sequentially, which means that the second rover 
 * to move until the first one has finished moving.
 * 
 * OUTPUT:
 * 
 * The output for each rover should be its final co-ordinates and heading.
 */ 

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include "glib.h"

#define ROVERS_MAX 100
#define ORDER_LEN_MAX 256
#define NR_ORDER 100

struct plateau {
	int32_t x_max;
	int32_t y_max;
	int (*is_in_func)(struct plateau *plateau, int32_t x, int32_t y); 
} plateau;

struct rover {
	uint32_t x;
	uint32_t y;
	char orient;
	char order[ORDER_LEN_MAX];
	struct plateau *plateau;
	int (*exec_order_func)(struct rover *r, char *order);	
} rovers[ROVERS_MAX];

uint32_t rovers_alive = 0;

struct NASA {
	char *order;
	char *feedback;
	char *(*get_order_func)(struct NASA *nasa, char **order);
	int (*put_feedback_func)(struct NASA *nasa, char *feedback);
} nasa;

static int is_in_plateau(struct plateau *plateau, int32_t x, int32_t y)
{
	if (!plateau) {
		printf("func: %s: Param error: plateau\n", __func__);
		return 0;
	}
	return (x >= 0 && x <= plateau->x_max && y >= 0 && y <= plateau->y_max);
}

static int deal_L(struct rover *r)
{
	switch (r->orient) {
		case 'N': 
			r->orient = 'W'; break;
		case 'S':
			r->orient = 'E'; break;
		case 'E':
			r->orient = 'N'; break;
		case 'W':
			r->orient = 'S'; break;
		default:
			printf("func:%s orientation error\n", __func__);
			return -1;
	}
	return 0;
}

/* deal_R */
static int deal_R(struct rover *r)
{
	switch (r->orient) {
		case 'N': 
			r->orient = 'E'; break;
		case 'S':
			r->orient = 'W'; break;
		case 'E':
			r->orient = 'S'; break;
		case 'W':
			r->orient = 'N'; break;
		default:
			printf("func:%s orientation error\n", __func__);
			return -1;
	}
	return 0;
}

/* deal_M */
static int deal_M(struct rover *r)
{
	switch (r->orient) {
		case 'N': 
			if (is_in_plateau(r->plateau, r->x, r->y + 1)) {
				r->y += 1;
			} else {
				printf("Error: out of bounds\n");
				return -1;
			}
			break;
		case 'S':
			if (is_in_plateau(r->plateau, r->x, r->y - 1)) {
				r->y -= 1;
			} else {
				printf("Error: out of bounds\n");
				return -1;
			}
			break;
		case 'E':
			if (is_in_plateau(r->plateau, r->x + 1, r->y)) {
				r->x += 1;
			} else {
				printf("Error: out of bounds\n");
				return -1;
			}
			break;
		case 'W':
			if (is_in_plateau(r->plateau, r->x - 1, r->y)) {
				r->x -= 1;
			} else {
				printf("Error: out of bounds\n");
				return -1;
			}
			break;
		default:
			printf("func: %s orientation error\n", __func__);
			return -1;
	}
	return 0;
}

/* deal_instruction */
static int deal_instruction(struct rover *r, char *order)
{
	char *p = order;

	while (*p != '\0') {
		switch (*p) {
			case 'L':
				if (deal_L(r) < 0) {
					return -1;
				}
				break;
			case 'R':
				if (deal_R(r) < 0) { 
					return -1;
				}
				break;
			case 'M':
				if (deal_M(r) < 0) {
					return -1;
				} 
				break;
			default:
				printf("error instruction\n");
		}
		p++;
	}
	return 0;
}


static char *get_order(struct NASA *nasa, char **order)
{
	if (!(nasa->order)) {
		printf("func:%s Param nasa error\n", __func__);
		return NULL;
	}
	*order = (char *)malloc(ORDER_LEN_MAX);
	if (!(*order)) {
		printf("func:%s malloc error\n", __func__);
		return NULL;
	}
	if (!strncpy(*order, nasa->order, ORDER_LEN_MAX)) {
		printf("func:%s get order error\n", __func__);
		return NULL;
	}
	return *order;
}

static int put_feedback(struct NASA *nasa, char *feedback)
{
	if (!feedback) {
		printf("func:%s param error\n", __func__);
		return -1;
	}
	
	if (!nasa->feedback) {
		nasa->feedback = (char *)malloc(ORDER_LEN_MAX);
	}

	if (!strncpy(nasa->feedback, feedback, ORDER_LEN_MAX)) {
		printf("func:%s set feedback\n", __func__);
		return -1;
	}

	return 0;
}

static int init_system(void)
{
	printf("init...\n");
	printf("Please set the scale of the plateau(e.g. '5 5'):\n");
	scanf("%d%d", &(plateau.x_max), &(plateau.y_max));
	if (plateau.x_max < 0 || plateau.y_max < 0) {
		printf("Init error: plateau\n");
		return -1;
	}
	plateau.is_in_func = is_in_plateau;

	nasa.get_order_func = get_order;
	nasa.put_feedback_func = put_feedback;
	nasa.order = malloc(ORDER_LEN_MAX);
	nasa.feedback = malloc(ORDER_LEN_MAX);
	
	return 0;
}

static void deinit_system(void)
{
	if (nasa.order) {
		free(nasa.order);
	}
	if (nasa.feedback) {
		free(nasa.feedback);
	}
}

static int input_instructions(struct NASA *nasa)
{
	char c;
	printf("Please input the instructions:\ne.g:\n5 5 N#LMLMLLMR$2 3 S#LML:\n");
	while (getchar() != '\n');
	gets(nasa->order);
	return 0;
}

static int parse_instructions(struct NASA *nasa)
{
	char *order = NULL;
	int i, j;
	gchar **rov, **ins, **loc;
	
	if (!nasa->get_order_func(nasa, &order)) {
		return -1;
	}
	char *p = order;
	rovers_alive = 0;
	
	while (*p != '\0') {
		if (*p == '$')
			rovers_alive++;
		p++;
	}
	rovers_alive++;
	
	rov = g_strsplit(order, "$", rovers_alive);
	if (g_strv_length(rov) != rovers_alive) {
		printf("parse rov error\n");
		return -1;
	}
	
	for (i = 0; i < rovers_alive; i++) {
		ins = g_strsplit(rov[i], "#", 2);
		if (!ins[1] || !ins[0]) {
			printf("Error: input\n");
			return -1;
		}
		strncpy(rovers[i].order, ins[1], ORDER_LEN_MAX);
		loc = g_strsplit(ins[0], " ", 3);
		if (!loc[0] || !loc[1] || !loc[2]) {
			printf("Error: input\n");
			return -1;
		}
		rovers[i].x = atoi(loc[0]);
		rovers[i].y = atoi(loc[1]);
		rovers[i].orient = *loc[2];
		rovers[i].plateau = &plateau;
		if (!rovers[i].plateau->is_in_func(rovers[i].plateau, rovers[i].x, 
					rovers[i].y)) {
			printf("Error: %s\n", __func__);
			return -1;
		}
		rovers[i].exec_order_func = deal_instruction;
	}
	return 0;
}

static int exec_order(void)
{
	int i;
	for (i = 0; i < rovers_alive; i++) {
		if (0 > deal_instruction(&rovers[i], rovers[i].order)) {
			printf("func:%s\n", __func__);
			return -1;
		}
	}
	return 0;
}

static void output_result(struct NASA *nasa)
{
	int i;
	char *pfb = NULL;
	char tmp[ORDER_LEN_MAX];
	for (i = 0; i < rovers_alive; i++) {
		if (0 > sprintf(tmp,"%d", rovers[i].x)) {
			printf("func:%s\n", __func__);
			return;
		}
		if (!pfb) {
			pfb = (char *)g_strconcat(tmp, NULL);
		} else {
			pfb = (char *)g_strconcat(pfb, tmp, NULL);
		}
		if (0 > sprintf(tmp, "%d", rovers[i].y)) {
			printf("func:%s\n", __func__);
			return;
		}
		pfb = (char *)g_strconcat(pfb, " ", tmp, NULL);
		sprintf(tmp, "%c", rovers[i].orient);
		pfb = g_strconcat(pfb, " ", tmp, "\n", NULL);
	}
	if (nasa->put_feedback_func(nasa, pfb)) {
		printf("func:%s put feedback error\n", __func__);
		return;
	}

	printf("OUTPUT:\n%s", nasa->feedback);
}

int main(int argc, char **argv)
{
	if (init_system()) {
		return -1;
	}

	input_instructions(&nasa);
	if (parse_instructions(&nasa)) {
		return -1;
	}
	if (exec_order()) {
		return -1;
	}
	output_result(&nasa);
	deinit_system();
	return 0;
}
